package com.alinesno.cloud.base.boot.service.impl;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.lang.exception.RpcServiceRuntimeException;
import javax.transaction.Transactional;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import com.alinesno.cloud.base.boot.entity.ManagerApplicationEntity;
import com.alinesno.cloud.base.boot.entity.ManagerSourceGenerateEntity;
import com.alinesno.cloud.base.boot.service.IManagerApplicationService;
import com.alinesno.cloud.base.boot.service.IManagerSourceGenerateService;
import com.alinesno.cloud.base.boot.utils.GeneratorSourceTool;
import com.alinesno.cloud.base.boot.utils.JenkinsTool;
import com.alinesno.cloud.common.core.service.impl.IBaseServiceImpl;
import com.alinesno.cloud.common.facade.wrapper.RestWrapper;
import com.alinesno.cloud.compoment.generate.Generator;
import com.alinesno.cloud.compoment.generate.generator.tools.GitTools;
import com.alinesno.cloud.compoment.generate.generator.tools.JenkinsJobBean;
import com.alinesno.cloud.compoment.generate.generator.tools.JenkinsTools;
import com.alinesno.cloud.compoment.generate.generator.tools.ZipUtils;
import com.offbytwo.jenkins.JenkinsServer;
import com.offbytwo.jenkins.model.JobWithDetails;

import cn.hutool.http.HtmlUtil;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author WeiXiaoJin
 * @since 2019-04-17 07:44:37
 */
@Service
public class ManagerSourceGenerateServiceImpl extends IBaseServiceImpl<ManagerSourceGenerateEntity, String> implements IManagerSourceGenerateService {

	// 日志记录
	private static final Logger log = LoggerFactory.getLogger(ManagerSourceGenerateServiceImpl.class);

	@Autowired
	private IManagerApplicationService managerApplicationService ;
	
	@Autowired
	private JenkinsTool jenkinsTool ; 
	
	/**
	 * 更新状态
	 */
//	@Override
//	public Page<ManagerSourceGenerateEntity> findAllByWrapperAndPageable(RestWrapper restWrapper) {
//		Page<ManagerSourceGenerateEntity> page = super.findAllByWrapperAndPageable(restWrapper);
//		List<ManagerSourceGenerateEntity> list = page.getContent() ; 
//		for(ManagerSourceGenerateEntity e : list) {
//			try {
//				e.setDockerBuildStatus(isBuildDockerSuccess(e));
//			} catch (IOException e1) {
//				log.error("获取{}，状态:" , ToStringBuilder.reflectionToString(e) , e1);
//			} 
//		}
//		return page ; 
//	}

	@Override
	public boolean buildDockerStatus(String id) throws IOException {
		ManagerSourceGenerateEntity dto = jpa.findById(id).get();
		return this.buildDockerStatus(dto);
	}
	
	@Override
	public String isBuildDockerSuccess(String id) throws IOException {
		ManagerSourceGenerateEntity dto = jpa.findById(id).get();
		return this.isBuildDockerSuccess(dto) ; 
	}
	
	@Override
	public String isBuildDockerSuccess(ManagerSourceGenerateEntity e) throws IOException {
		String jobName = e.getJenkinsJobname() ; //.getFeignServerPath() ; 
		if(jobName == null) {
			return BUILD_DOCKER_STATUS_NORMARL ; 
		}
		JobWithDetails job = jenkinsTool.getInstance().getJob(jobName) ; 
		if(job != null) {
			boolean isInQueue = job.isInQueue() ; 
			if(isInQueue) {
				return BUILD_DOCKER_STATUS_QUEUE ; 
			} else {
				
				boolean isFinish = jenkinsTool.isFinished(job.getLastBuild().getNumber(), jobName) ; 
				if(isFinish) {
					boolean isSuccess = jenkinsTool.isSuccess(jobName, job.getLastBuild().getNumber()) ; 
					if(isSuccess) {
						return BUILD_DOCKER_STATUS_SUCCESS ; 
					}else {
						return BUILD_DOCKER_STATUS_FAIL; 
					}
				} else {
					return BUILD_DOCKER_STATUS_DOING ; 
				}
			}
		}
		return BUILD_DOCKER_STATUS_NORMARL ; 
	}
	
	private boolean buildDockerStatus(ManagerSourceGenerateEntity e) throws IOException {
		String jobName = e.getFeignServerPath() ; 
		JobWithDetails job = jenkinsTool.getInstance().getJob(jobName) ; 
		if(job != null) {
			return jenkinsTool.isFinished(
					job.getLastBuild().getNumber(), 
					jobName) ; 
		}else {
			return true ; 
		}
	}

	/**
	 * 验证数据库和git仓库地址
	 * @param dto
	 */
	private void validateDbAndGit(ManagerSourceGenerateEntity dto) {
		// 验证数据库和git的可用性_start
		boolean dbBnable = this.checkDatabaseUseable(dto.getDbUrl(), dto.getDbUser() , dto.getDbPwd()) ; 
		Assert.isTrue(dbBnable , "数据库【"+dto.getDbUrl()+"】连接异常.") ; 
		
		boolean gitEnable = this.checkGitrepositoryUseable(dto.getGitRepositoryUrl(), dto.getGitUserName(), dto.getGitPassword()) ; 
		Assert.isTrue(gitEnable , "仓库【"+dto.getGitRepositoryUrl()+"】连接异常.") ; 
		// 验证数据库和git的可用性_end 
	}

	@Override
	public byte[] generatorCode(String id) throws IOException {
		log.debug("生成代码:{}", id);

		Assert.hasLength(id, "主键不能为空.");
		Date date = new Date();

		ManagerSourceGenerateEntity dto = jpa.findById(id).get();
		validateDbAndGit(dto) ; 
		
		// 转义DBURL
		dto.setDbUrl(HtmlUtil.unescape(dto.getDbUrl()));

		String downPath = "/tmp/" + new SimpleDateFormat("yyyy/MM").format(date) + "/" + dto.getFeignServerPath();
		File f = new File(downPath);

		log.debug("downPath:{}", downPath);

		if (f.exists()) {
			FileUtils.forceDelete(f);
		}
		FileUtils.forceMkdir(f);

		Generator g = new GeneratorSourceTool(dto, downPath);
		g.generator(null);
		String targetPath = downPath + "-" + System.currentTimeMillis() + ".zip";
		ZipUtils.toZip(downPath, targetPath, true);

		// 删除临时仓库地址
		FileUtils.forceDelete(f);
		log.debug("删除临时代码成功.");
		
		byte[] data = FileUtils.readFileToByteArray(new File(targetPath));
		
		return data ; 
	}

	@Override
	public String git(String id) throws IOException {
		
		ManagerSourceGenerateEntity dto = jpa.findById(id).get();
		validateDbAndGit(dto) ; 
		
		if(StringUtils.isBlank(dto.getGitRepositoryUrl())) {
			return "ERROR" ; 
		}

		String downPath = "/tmp/git-" + System.currentTimeMillis();
		File file = new File(downPath);
		
		if(!file.exists()) {
			FileUtils.forceMkdir(file);
		}

		log.debug("正在克隆远程仓库.");
		boolean cloneResult = GitTools.cloneGit(dto.getGitRepositoryUrl(), file , dto.getGitUserName(), dto.getGitPassword());
		Assert.isTrue(cloneResult, "远程仓库克隆失败.");

		// 生成代码
		Generator g = new GeneratorSourceTool(dto, downPath);
		g.generator(null);

		log.debug("正在提交代码到远程仓库.");
		boolean commitResult;
		
		try {
			
			commitResult = GitTools.commitAndPush(file, dto.getGitUserName(), dto.getGitPassword(), "代码生成器自动生成【"+dto.getApplicationName()+"】代码并提交项目代码");
			Assert.isTrue(commitResult, "远程仓库提交失败.");
			
		} catch (NoFilepatternException e) {
			log.debug("代码生成路径异常:{}" , e);
			throw new RpcServiceRuntimeException("代码生成路径异常") ; 
		} catch (GitAPIException e) {
			log.debug("代码提交异常:{}" , e);
			throw new RpcServiceRuntimeException("代码提交异常,请确认路径或者账号正确") ; 
		}

		// 删除临时仓库地址
		// FileUtils.forceDelete(file);
		log.debug("删除临时目录代码【{}】成功." , file.getAbsoluteFile());

		return null;
	}

	@Override
	public String jenkins(String id) throws IOException, URISyntaxException {
		
		ManagerSourceGenerateEntity dto = jpa.findById(id).get();
		if(StringUtils.isBlank(dto.getJenkinsUrl())) {
			return "ERROR" ; 
		}

		String credentialsId = "a3164ab0-d246-4d5f-a852-1d775e879599";

		JenkinsJobBean bean = new JenkinsJobBean();
		bean.setCredentialsId(credentialsId);
		bean.setDescription(dto.getApplicationName());
		bean.setDocker(dto.isDockerProject());
		bean.setGitBranch("master"); // 默认master节点
		
		bean.setGitUrl(dto.getGitRepositoryUrl()) ; 
		bean.setJobName(dto.getJenkinsJobname()) ;

		String mvnGoals = "clean package spring-boot:repackage -U ";
		if (bean.isDocker()) {
			mvnGoals += "docker:build ";
		}
		bean.setGoals(mvnGoals);

		String url = dto.getJenkinsUrl() ; 
		String username = dto.getJenkinsUserName() ; 
		String password = dto.getJenkinsPassword() ; 

		JenkinsTools.buildMavenJob(bean, url, username, password);
		
		return bean.getJobName() ; 
	}

	@Transactional
	@Override
	public Map<String, String> projectAutoBuild(ManagerApplicationEntity app, ManagerSourceGenerateEntity source) {

		Assert.notNull(app , "应用不能为空.");
		Assert.notNull(source, "源码应用不能为空.");
		
		Map<String, String> resultMap = new HashMap<String, String>() ; 
		
		app = managerApplicationService.save(app) ; 
		source = jpa.save(source) ; 
		
		try {
			
			if(StringUtils.isNotBlank(source.getGitRepositoryUrl())) {
				git(source.getId()) ; 
				resultMap.put("GIT_PATH", source.getGitRepositoryUrl()) ; 
			}
			
			if(StringUtils.isNotBlank(source.getJenkinsUrl())) {
				jenkins(source.getId()) ;
				resultMap.put("JENKINS_PATH", source.getJenkinsUrl()) ; 
				resultMap.put("DOCKER_PATH", source.getJenkinsUrl()) ; 
			}
		
			
		} catch (IOException e) {
			log.debug("文件读取异常:{}" , e);
			
			throw new RpcServiceRuntimeException("文件读取异常.") ; 
		} catch (URISyntaxException e) {
			log.debug("请求链接地址错误:{}" , e);
			
			throw new RpcServiceRuntimeException("请求链接地址错误") ; 
		} 
		
		return null;
	}

	@Override
	public String buildDocker(String id, boolean isPublic) throws IOException {
		ManagerSourceGenerateEntity dto = jpa.findById(id).get();
		String gitRepo = dto.getGitRepositoryUrl() ; 
		
		Assert.hasLength(dto.getGitRepositoryUrl() ,"代码库地址不能为空.");
		
		StringBuffer gitUrl = new StringBuffer() ;
		String httpHead = gitRepo.startsWith("https://")?"https://":"http://" ; 
		
		gitUrl.append(httpHead) ; 
		gitUrl.append((dto.getGitUserName()+":"+dto.getGitPassword()).replaceAll("@", "%40")) ; 
		gitUrl.append("@") ; 
		gitUrl.append(dto.getGitRepositoryUrl().replaceFirst(httpHead, "")) ; 
		
		log.debug("git url = {}" , gitUrl.toString());
		
		JenkinsServer jenkins = jenkinsTool.getInstance() ; 
		
		String jobName = "alinesno-job-temp-" + System.currentTimeMillis() ;
		JobWithDetails job = jenkins.getJob(jobName);
		
		if (job == null) {
			Map<String , Object> map = new HashMap<String , Object>() ; 
			map.put("GIT_PATH", gitUrl.toString()) ; 
			jenkins.createJob(jobName, jenkinsTool.getJobXml(map), true);
		}

		job = jenkins.getJob(jobName);
		job.build() ; 
	
		// 设置镜像的名称
		dto.setJenkinsJobname(jobName); ; 
		dto.setDockerPath(jenkinsTool.getDockerPath(dto.getFeignServerPath())); 
		dto.setDockerBuildStatus(BUILD_DOCKER_STATUS_DOING);
		return dto.getDockerPath() ;
	}

	
	@Override
	public boolean checkDatabaseUseable(String dbUrl, String dbUser, String dbPwd) {
		Connection conn = null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection(dbUrl, dbUser, dbPwd);//连接数据库
			return true ; 
		} catch (SQLException e) {
			log.error("数据库["+dbUrl+"]连接异常:"+e);
			String msg = e.getMessage() ; 
			if(msg.startsWith("Unknown database")) {
				throw new RpcServiceRuntimeException("数据库不存在.") ;
			}else if(msg.startsWith("Access denied")) {
				throw new RpcServiceRuntimeException("用户名密码不正确.") ;
			}else if(msg.startsWith("The server time zone value")) {
				throw new RpcServiceRuntimeException("数据库连接请添加参数【serverTimezone=GMT】") ;
			}else {
				throw new RpcServiceRuntimeException("连接数据库异常.") ;
			}
		} catch (ClassNotFoundException e) {
			log.error("数据库["+dbUrl+"]连接异常:"+e);
			throw new RpcServiceRuntimeException("连接数据库驱动异常.") ;
		}finally {
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					log.error("数据库关闭异常.");
				}
			}
		}
	}
	
	@Override
	public boolean checkGitrepositoryUseable(String gitRepositoryUrl, String gitUserName, String gitPassword) {
		CredentialsProvider credentialsProvider = new UsernamePasswordCredentialsProvider(gitUserName,gitPassword);
		
		int count = this.findAll(new RestWrapper().eq("gitRepositoryUrl", gitRepositoryUrl)).size() ; 
		Assert.isTrue(count == 0, "仓库已存在.");
		
		try {
			Map<String, Ref> asMap = Git.lsRemoteRepository()
			       .setRemote(gitRepositoryUrl)
			       .setCredentialsProvider(credentialsProvider)
			       .callAsMap();
			log.debug("asMap = {}" , asMap);
			return true ; 
		} catch (GitAPIException e) {
			log.error("Git仓库["+gitRepositoryUrl+"]连接异常:"+e);
			String msg = e.getMessage() ; 
			if(msg != null && msg.contains("not authorized")) {
				throw new RpcServiceRuntimeException("仓库认证失败，请确认用户名密码是否正确.") ; 
			}else if(msg != null && msg.contains("Not Found")) {
				throw new RpcServiceRuntimeException("仓库地址地址不存在，请确认地址是否可以正常访问.") ; 
			}else {
				throw new RpcServiceRuntimeException("仓库连接连接异常") ; 
			}
		}
	}

	@Override
	public boolean buildK8S(String id, String env) {
		return false;
	}

}
